package com.expoente.view;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import com.expoente.utils.MyOverlay;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.location.Address;
import android.location.Criteria;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Toast;

import com.expoente.model.LineInformation;
import com.expoente.model.Place;
import com.expoente.model.dao.impl.DBAdapterImpl;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;

public class GoogleMapsActivity
		extends MapActivity {

	MapView mapView;
	MapController mc;
	GeoPoint p;
	List<GeoPoint> p2 = new ArrayList<GeoPoint>();

	class MapOverlay
			extends Overlay {
		@Override
		public boolean draw(Canvas canvas, MapView mapView, boolean shadow,
				long when) {
			super.draw(canvas, mapView, shadow);

			// ---translate the GeoPoint to screen pixels---
			Point screenPts = new Point();
			Point screenPts2 = new Point();

			Bitmap bmp = BitmapFactory.decodeResource(getResources(),
					R.drawable.pushpin);

			mapView.getProjection().toPixels(p, screenPts);
			canvas.drawBitmap(bmp, screenPts.x, screenPts.y - 50, null);

			for (int i = 0; i < p2.size(); i++) {
				mapView.getProjection().toPixels(p2.get(i), screenPts2);
				canvas.drawBitmap(bmp, screenPts2.x, screenPts2.y - 50, null);
			}

			return true;
		}

		@Override
		public boolean onTouchEvent(MotionEvent event, MapView mapView) {
			// ---when user lifts his finger---
			if (event.getAction() == 1) {
				GeoPoint p = mapView.getProjection().fromPixels(
						(int) event.getX(), (int) event.getY());
				Toast.makeText(
						getBaseContext(),
						p.getLatitudeE6() / 1E6 + "," + p.getLongitudeE6()
								/ 1E6, Toast.LENGTH_SHORT).show();
			}
			return false;
		}
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.map_screen);

		DBAdapterImpl dbAdapter = new DBAdapterImpl(this);
		dbAdapter.open();

		Intent intent = getIntent();
		LineInformation lineInformation = (LineInformation) intent
				.getSerializableExtra("item");
		lineInformation = dbAdapter.getLineInformation(lineInformation
				.getLine().get_id());

		mapView = (MapView) findViewById(R.id.map_view);
		mapView.setBuiltInZoomControls(true);

		mapView.setClickable(true);
		mc = mapView.getController();
		Location myLocation = getLocation();
		Location destLocation = new Location(LocationManager.PASSIVE_PROVIDER);

		Geocoder geoCoder = new Geocoder(getBaseContext(), Locale.getDefault());
		try {

			List<Place> places = lineInformation.getPlaces();
			String rota[] = new String[places.size()];
			for (int i = 0; i < places.size(); i++) {
				rota[i] = places.get(i).getAddress();
			}
			List<Address> pontoInicial = new ArrayList<Address>();
			if (places.get(0).getLatitude() != 0
					&& places.get(0).getLongitude() != 0) {
				Address address = new Address(null);
				address.setLatitude(places.get(0).getLatitude());
				address.setLongitude(places.get(0).getLongitude());
				pontoInicial.add(address);
			} else {
				pontoInicial = geoCoder.getFromLocationName(rota[0], 5);
				dbAdapter.updatePlaceLatLong(places.get(0).get_id(),
						pontoInicial.get(0).getLatitude(), pontoInicial.get(0)
								.getLongitude());
			}
			if (pontoInicial.size() > 0) {
				destLocation.setLatitude(pontoInicial.get(0).getLatitude());
				destLocation.setLongitude(pontoInicial.get(0).getLongitude());
				p = new GeoPoint(
						(int) (pontoInicial.get(0).getLatitude() * 1E6),
						(int) (pontoInicial.get(0).getLongitude() * 1E6));
				for (int i = 1; i < rota.length; i++) {
					List<Address> destino = new ArrayList<Address>();
					if (places.get(i).getLatitude() != 0
							&& places.get(i).getLongitude() != 0) {
						Address address = new Address(null);
						address.setLatitude(places.get(i).getLatitude());
						address.setLongitude(places.get(i).getLongitude());
						destino.add(address);
					} else {
						destino = geoCoder.getFromLocationName(rota[i], 5);
						dbAdapter.updatePlaceLatLong(places.get(i).get_id(),
								destino.get(0).getLatitude(), destino.get(0)
										.getLongitude());
					}
					p2.add(new GeoPoint(
							(int) (destino.get(0).getLatitude() * 1E6),
							(int) (destino.get(0).getLongitude() * 1E6)));
				}
			}
			dbAdapter.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		drawRoute(p, p2, Color.GREEN, mapView);
		mc.animateTo(p);
		mc.setZoom(17);

		MapOverlay mapOverlay = new MapOverlay();
		List<Overlay> listOfOverlays = mapView.getOverlays();
		listOfOverlays.add(mapOverlay);

		mapView.invalidate();
	}

	@Override
	protected boolean isRouteDisplayed() {
		return true;
	}

	private void drawRoute(GeoPoint src, List<GeoPoint> dest, int color,
			MapView mMapView01) {
		StringBuilder urlString = new StringBuilder();
		urlString
				.append("http://maps.google.com/maps/api/directions/xml?origin=");
		urlString.append(Double.toString((double) src.getLatitudeE6() / 1.0E6));
		urlString.append(",");
		urlString
				.append(Double.toString((double) src.getLongitudeE6() / 1.0E6));
		urlString.append("&destination=");
		urlString.append(Double.toString((double) dest.get(dest.size() - 1)
				.getLatitudeE6() / 1.0E6));
		urlString.append(",");
		urlString.append(Double.toString((double) dest.get(dest.size() - 1)
				.getLongitudeE6() / 1.0E6));
		if (dest.size() > 1) {
			urlString.append("&waypoints=");
			for (int i = 0; i < dest.size() - 1; i++) {
				if (i != 0) {
					urlString.append("|");
				}
				urlString.append(Double.toString((double) dest.get(i)
						.getLatitudeE6() / 1.0E6));
				urlString.append(",");
				urlString.append(Double.toString((double) dest.get(i)
						.getLongitudeE6() / 1.0E6));
			}
		}
		urlString.append("&sensor=false");
		Log.d("PATH", urlString.toString());
		Document doc = null;
		HttpURLConnection urlConnection = null;
		URL url = null;
		try {
			url = new URL(urlString.toString());
			urlConnection = (HttpURLConnection) url.openConnection();
			urlConnection.setRequestMethod("GET");
			urlConnection.setDoOutput(true);
			urlConnection.setDoInput(true);
			urlConnection.connect();

			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			doc = db.parse(urlConnection.getInputStream());

			if (doc.getElementsByTagName("points").getLength() > 0) {
				List<GeoPoint> lista = new ArrayList<GeoPoint>();
				for (int i = 0; i < doc.getElementsByTagName("points")
						.getLength() - 1; i++) {
					for (GeoPoint geoPoint : decodePoly(doc
							.getElementsByTagName("points").item(i)
							.getFirstChild().getNodeValue())) {
						lista.add(geoPoint);
					}
				}
				// Log.d("LISTA", lista.toString());
				String path = lista.toString().replace("[", "")
						.replace("]", "");
				String[] pairs = path.split(" ");
				String[] lngLat = pairs[0].split(",");
				int lat1 = Integer.parseInt(lngLat[0]);
				int lng1 = Integer.parseInt(lngLat[1]);

				GeoPoint startGP = new GeoPoint(lat1, lng1);
				mMapView01.getOverlays()
						.add(new MyOverlay(startGP, startGP, 1));
				GeoPoint gp1;
				GeoPoint gp2 = startGP;
				for (int i = 1; i < pairs.length; i++) // the last one would be
				// crash
				{
					lngLat = pairs[i].split(",");
					gp1 = gp2;
					lat1 = Integer.parseInt(lngLat[0]);
					lng1 = Integer.parseInt(lngLat[1]);
					// watch out! For GeoPoint, first:latitude, second:longitude
					gp2 = new GeoPoint(lat1, lng1);
					mMapView01.getOverlays().add(
							new MyOverlay(gp1, gp2, 2, color));
					// Log.d("xxx", "pair:" + pairs[i]);
				}
				mMapView01.getOverlays().add(
						new MyOverlay(dest.get(dest.size() - 1), dest.get(dest
								.size() - 1), 3)); // use
			}

		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		}
	}

	private List<GeoPoint> decodePoly(String encoded) {

		List<GeoPoint> poly = new ArrayList<GeoPoint>();
		int index = 0, len = encoded.length();
		int lat = 0, lng = 0;

		while (index < len) {
			int b, shift = 0, result = 0;
			do {
				b = encoded.charAt(index++) - 63;
				result |= (b & 0x1f) << shift;
				shift += 5;
			} while (b >= 0x20);
			int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
			lat += dlat;

			shift = 0;
			result = 0;
			do {
				b = encoded.charAt(index++) - 63;
				result |= (b & 0x1f) << shift;
				shift += 5;
			} while (b >= 0x20);
			int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
			lng += dlng;

			GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6),
					(int) (((double) lng / 1E5) * 1E6));
			poly.add(p);
		}

		return poly;
	}

	private boolean getDistance(double startLtd, double startLng,
			double endLtd, double endLng) {
		double result = 6380 * (Math.acos(Math.cos(Math.toRadians(startLtd))
				* Math.cos(Math.toRadians(endLtd))
				* Math.cos(Math.toRadians(startLng) - Math.toRadians(endLng))
				+ Math.sin(Math.toRadians(startLtd))
				* Math.sin(Math.toRadians(endLtd))));
		if (result < 0.500) {
			return true;
		}
		return false;
	}

	private Location getLocation() {
		LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

		LocationListener locationListener = new LocationListener() {
			public void onLocationChanged(Location location) {

			}

			public void onStatusChanged(String provider, int status,
					Bundle extras) {
			}

			public void onProviderEnabled(String provider) {
			}

			public void onProviderDisabled(String provider) {
			}
		};

		Criteria criteria = new Criteria();
		criteria.setAccuracy(Criteria.ACCURACY_FINE);
		String provider = locationManager.getBestProvider(criteria, true);
		locationManager.requestLocationUpdates(
				locationManager.NETWORK_PROVIDER, 1, 0, locationListener);
		Location mostRecentLocation = locationManager
				.getLastKnownLocation(provider);
		return mostRecentLocation;
	}
}